package component;

import java.util.ArrayList;

import Prism.core.AbstractImplementation;
import Prism.core.Event;
import Prism.core.PrismConstants;
import basicTypes.TaskID;

public class NossTransManager extends AbstractImplementation {
	
	// prov Name(Event Name)
	public static final String provGetAvailablePhoneNumber = "getAvailablePhoneNumber";
	public static final String provAddNewNumber = "addNewNumber";
	public static final String provDeleteExistingNumber = "deleteExistingNumber";
	public static final String provLockResource = "lockResource";
	public static final String provUnlockResource = "unlockResource";
	
	// freePhoneNumber: TaskID -> PhoneNumber;//TaskID used for representatives' IDs
	public String freePhoneNumber(String taskID) {
		int size = taskIDs.size();
		for(int i=0; i<size; i++) {
			if(taskIDs.get(i).getTaskID().equals(taskID)) {
				return taskIDs.get(i).getPhoneNumber();
			}
		}
		
		String freeno;
		do {
			
			freeno=String.valueOf((int)(Math.random()*1000000000));
		} while(usedPhoneNumbers.contains(freeno) || resourcesLocked.contains(freeno));
		
		return freeno;
	}
	
	// usedPhoneNumbers: \set PhoneNumber;
	ArrayList<String> usedPhoneNumbers;
	
	// resorcesLocked: \set PhoneNumber;
	ArrayList<String> resourcesLocked;
	
	ArrayList<TaskID> taskIDs;
	
	// Check invariant property
	public boolean checkInvariant() {
		//#usedPhoneNumbers \eqgreater 0;
		//#resourcesLocked \eqgreater 0;
		if(usedPhoneNumbers.size()>=0 && resourcesLocked.size()>=0) return true;
		return false;
	}
	
	@Override
	public void start() {
		super.start();
		
		//Initialize
		usedPhoneNumbers = new ArrayList<String>();
		resourcesLocked = new ArrayList<String>();
		taskIDs = new ArrayList<TaskID>();
	}
	
	@Override
	public void handle(Event e) {
		// Handle of request of provGetAvailablePhoneNumber
		if(e.getName().equals(provGetAvailablePhoneNumber)) {
			String freeno = freePhoneNumber((String)e.getParameter("taskID"));
			
			//post (\result = freePhoneNumber(tid));
			Event n = new Event(provGetAvailablePhoneNumber, PrismConstants.REPLY);
			n.addParameter("phoneNumber", freeno);
			this.send(n);
		}
		// Handle of request of provAddNewNumber
		else if(e.getName().equals(provAddNewNumber)) {
			addNumber((String)e.getParameter("phoneNumber"));
			
		}
		// Handle of request of provDeleteExistingNumber
		else if(e.getName().equals(provDeleteExistingNumber)) {
			deleteNumber((String)e.getParameter("phoneNumber"));
		}
		// Handle of request of provLockResource
		else if(e.getName().equals(provLockResource)) {
			String taskID = (String) e.getParameter("taskID");
			String phoneNumber = (String) e.getParameter("phoneNumber");
			
			// pre id \not_in resourcesLocked;
			if(resourcesLocked.contains(phoneNumber)==false) {
				// post (id \in resourcesLocked) 
				// \and (freePhoneNumber(tid)=id);
				resourcesLocked.add(phoneNumber);
				taskIDs.add(new TaskID(taskID,phoneNumber));
			}
		}
		// Handle of request of provUnlockResource
		else if(e.getName().equals(provUnlockResource)) {
			String taskID = (String) e.getParameter("taskID");
			String phoneNumber = (String) e.getParameter("phoneNumber");

			// pre (id \in resourcesLocked)
			// \and (freePhoneNumber(tid)=id);
			if( (resourcesLocked.contains(phoneNumber)
				&& (freePhoneNumber(taskID).equals(phoneNumber)))) {

				// post id \not_in resourcesLocked;
				resourcesLocked.remove(phoneNumber);

			}
 
		}
		// Handle of request reply of provGetPhoneNumbers
		else if(e.getName().equals(NossDatabase.provGetPhoneNumbers)) {
					
			String clearFlag = (String) e.getParameter("clearFlag");

			// Clear if data is first one of whole billing
			if(clearFlag.equals("true")) usedPhoneNumbers.clear();
			
			// Add billing
			usedPhoneNumbers.add((String) e.getParameter("phoneNumber"));
		}
	}

	// this is public, because initialize needed by outside of this instance
	public void getNumbers() {
		// send request to upper layer
		Prism.core.Event r = new Prism.core.Event(NossDatabase.provGetPhoneNumbers, PrismConstants.REQUEST);
		this.send(r);
	}
	
	// this is private, because only used by event
	private void addNumber( String phoneNumber ) {
		// send request to upper layer
		Prism.core.Event r = new Prism.core.Event(NossDatabase.provAddPhoneNumber, PrismConstants.REQUEST);
		r.addParameter("phoneNumber", phoneNumber);
		this.send(r);
	}
	
	// this is private, because only used by event
	private void deleteNumber( String phoneNumber ) {
		// send request to upper layer
		Prism.core.Event r = new Prism.core.Event(NossDatabase.provDeletePhoneNumber, PrismConstants.REQUEST);
		r.addParameter("phoneNumber", phoneNumber);
		this.send(r);
	}
}
